home *** CD-ROM | disk | FTP | other *** search
- PL/68K.DOC: The Dr. Dobb's Journal Article
-
- June 25, 1991
-
- The following is a slight revision of an article that appeared in the
- January, 1986 issue of Dr. Dobb's Journal. Alas, the figures are no
- longer available.
-
- This article gives the original vision behind the language. It does *not*
- describe the current language, which is CSTAR. (The PL/68K language
- evolved in the years 1986 and 1987 to become the CSTAR language.)
- See the files CSTAR.DOC and DESIGN.DOC for a discussion of how
- PL/68K became CSTAR.
-
-
- C BECOMES 68000 ASSEMBLY LANGUAGE
-
- One day not long ago, I became embroiled in an old debate with another
- programmer named Charlie...
-
- "The programming team I manage is about to start a big project," I said,
- "and I must decide which language to use."
-
- "Really? Which languages are you considering?"
-
- "C and 68000 assembly language. The product will have strong
- competition, and great performance is crucial, so it's reasonable to
- consider assembly language. On the other hand , C is so much easier to
- use.
-
- "Why don't you program in C and recode in assembly language as
- needed?" Charlie asked.
-
- "Of course I've considered that. It might work as far as execution speed
- is concerned, although I'm not sure. C doesn't let you allocate registers
- globally, and that's a big handicap. Speed is not the only problem,
- though. The code must be compact, but our C compiler produces code
- that is 50 percent larger than assembly language. No, there's no doubt
- about it--eventually the program will have to be written in assembly."
-
- "Do the initial prototyping in C. That's the right way," Charlie persisted.
- "When the program is finished recoding in assembly language will be
- much easier."
-
- "Hmmm. I'm not convinced. Recoding is going to be expensive; we'll
- end up debugging the whole program twice. There might even be
- pressure from higher management not to recode and come out with an
- inferior product."
-
- Charlie just snorted and walked away, muttering something about
- assembly language being a throw back to the dark ages.
-
-
- WRITING IN BOTH C AND ASSEMBLY LANGUAGE
-
- Fortunately, my friend John overheard this conversation. John and I have
- worked together for 15 years, and we enjoy discussing problems that
- come up on the job. John laughed, "Charlie is more interested in being
- right about C than in solving your problem."
-
- "You sound more sympathetic."
-
- "Well, your choice is crucial. Which language you use determines, to a
- large extent, how your project will turn out."
-
- "Yes. What bothers me most is that I've got to choose now, but I won't
- know until the project is almost over whether the choice was correct."
-
- "I think I know a way around this dilemma--it's a language I invented
- called PL/68K."
-
- "John, my only options are C and 68000 assembly language."
-
- "Don't be fooled by the name. PL/68K isn't really an independent
- language but a way of using C to do assembly-language programming."
-
- "John, you are not making sense!"
-
- "Let me explain. You can think of PL/68K as being either C or assembly
- language--either/or. But in fact, you can run a program written in
- PL/68K through both the PL/68K assembler and any standard C
- compiler. PL/68K is both C and assembly language at the same time."
-
- "Wait a minute. You are going much too fast," I said. "First of all, you
- can't possibly compile an assembly-language program with a C compiler!
- Assembly language doesn't look anything like C--the C compiler will
- spit out a thousand error messages!"
-
- "PL/68K doesn't resemble 'traditional' assembly language. Forget what
- assembly language usually looks like and ask yourself, 'What are the
- characteristics of assembly language?' "
-
- "Go on," I replied. "You tell me."
-
- "First, assembly language allows full access to all machine resources--all
- registers, all locations in memory (including the run-time stack), all I/O
- ports, all privilege modes, and all machine instructions. Second, there is
- a one-for-one correspondence between the source code you write and and
- the object code produced by the assembler. You always know what code
- a particular assembly-language construct generates; assemblers neither
- rearrange code nor 'optimize' code away nor add anything extraneous.
- Assemblers are very literal-minded. Thus, assembly language ensures
- zero time and space overhead."
-
- "You're saying that assembly language gives you complete control over
- the machine, without a compiler getting in the way."
-
- "Exactly. Now, suppose we say assembly language is any language that
- (1) allows complete access to all machine resources, (2) provides a clear
- correspondence between source code and object code, and (3) imposes
- zero time or space overhead."
-
-
- SEMANTIC IDENTITY
-
- "Hmmm," I mused. "This definition doesn't say what assembly language
- looks like. It could even look like C. But I still don't understand. If you
- run a PL/68K program through an assembler, you will get one program.
- If you run the same source through a C compiler, you will get a second
- program. The two programs are not going to do the same things--similar
- things, maybe, but not the same things. The fact that the source code is
- the same doesn't matter. To put it another way, given a result desired
- from a specific PL/68K program, we would still have to choose between
- assembling the program with the PL/68K assembler or compiling it with a
- C compiler."
-
- "You've stated the problem very well," John said, "but I have discovered
- that it's possible to design PL/68K so that the program produced by the
- PL/68K assembler will work in the same way as the program produced
- by the C compiler."
-
- "That sounds impossible!"
-
- "I don't think so. Let's turn the problem around. Suppose we design
- PL/68K according to what might be called 'the principle of semantic
- equivalence.' This principle states that a program, when assembled by
- the PL/68K assembler, must work in the same way as when it is compiled
- by a standard C compiler. Now let's ask ourselves, 'What needs to be
- eliminated from PL/68K to guarantee semantic equivalence?' " (See
- Figure 1.)
-
- "Tell me," I said, "how much of C is left after the principle of semantic
- equivalence takes its toll?"
-
- "Just about all of it. The preprocessor is identical to the C preprocessor.
- All declarations and structure statements are present. Function are
- unchanged, as are Boolean and relational operators and expressions.
- PL/68K is a little more fussy about types and type conversions than C is
- and there are some slight restrictions are placed on how complicated
- arithmetic expressions can be.
-
- "You keep talking about PL/68K being assembly language," I said.
- "How is it possible to produce code the quality of assembly language
- from a language that is a subset of C?"
-
- "I haven't shown you the whole language yet. Two other rules guide the
- design of PL/68K. These rules, together with the principle of semantic
- equivalence, determine the form and content of PL/68K. The two rules
- are 'the code selection rule'--the assembler for PL/68K does no code
- selection, and all arithmetic operations in PL/68K correspond to unique
- 68000 machine instructions; and 'the register allocation rule'--the
- assembler for PL/68K does no register allocation. The programmer must
- specify how registers will be allocated.
-
- "In short, these rules say that an assembler for PL/68K never has to make
- any significant decisions. Because the PL/68K assembler knows how to
- select code and allocate register, it will never need any of the fancy
- techniques used by optimizing compilers, but it will be able to produce
- code that is just as good as assembly language.
-
- "I like to think of the assembler for PL/68K as a simple compiler,
- consisting of a parser, a straightforward code generator and a peephole
- optimizer. The whole job should take about a year to complete rather than
- the 10 to 20 programmer years for a typical optimizing compiler. Using
- compiler technology to write an assembler was the initial idea that started
- me thinking about PL/68K."
-
- Now that I've presented the general ideas behind PL/68K, I'll drop this
- dialogue format and these fictional characters and look at the details of the
- language.
-
-
- SPECIFYING REGISTERS
-
- Because PL/68K is both assembly language and C, some way must be
- found to deal with assembly-language constructs such as registers,
- address modes, and individual machine instructions, while at the same
- time remaining compatible with C. These assembly-language constructs
- are represented by reserved words, shown in Table 1.
-
- As for the registers of the 68000, d0 through d7 stand for the data
- registers, a0 through a7 for the address registers, pc for the program
- counter, ssr for the status register, and ccr for the condition code register,
- which is the lower byte of the ssr.
-
- Standard aliases are also defined. Register a7 can also be called sp, ssp,
- or usp to denote the stack pointer (or system stack pointer or user stack
- pointer). The reserved words r0 through r7 are synonyms for d0 through
- d7, and the names r8 through r15 are synonyms for registers a0 through
- a7.
-
- All registers on the 68000 are 32 bits long (except the status registers),
- but not every instruction uses all 32 bits of a register. Besides long (32-
- bit) operations, byte-length (8-bit) and word-length (16-bit) operations are
- permitted on data registers, and word-length operations are permitted on
- address registers. To represent the length of an operation, the name of
- any data register can be followed by a 'b' to denote to denote byte length
- or w to denote word length. Thus, d0 stands for the long register d0,
- d0w stands for the word-length register d0, and d0b stands for the byte-
- length register d0. Address registers are treated in a similar manner,
- except that byte-length operations are not permitted.
-
-
- ADDRESS MODES
-
- The 68000 has 12 different address modes, or means of accessing
- operands. (See Table 2) The address modes are represented in PL/68K
- by five of C's operators, namely &, *, ++, P P, and P>.
-
- Let's look, for example, at the Address Register Indirect with
- Postincrement address mode. (It's a lot easier to use than to say.) This
- mode uses the contents of an address register as the address of an
- operand. After the operation is performed, the address register is
- incremented by 1, 2, or 4, depending on the size of the operation. In
- traditional assembly language, that mode applied to address register a0
- would be written as (a0)+. In PL/68K, that address modes is represented
- by *a0++. for example, you would write d0b = *a0++; in PL/68K
- instead of MOVE.B (a0)+, d0b.
-
- The word primitive denotes what is called an effective address in
- machine-language terms. A primitive describes an operand, which may
- be in a register, on the run-time stack, or in static memory. In PL/68K,
- the valid forms of primitives are determined by the address modes I've
- just discussed.
-
-
- DECLARATIONS
-
- In effect, declarations produce DC (define constant) and DS (define
- storage) pseudo-operations. (See Table 3) Although declarations produce
- no executable code they determine what code gets produced by arithmetic
- operators. For instance, if a is an integer, the assignment statement a *=
- b, which multiplies a by b, generates a MULS (signed multiply) machine
- instruction, but if a is an unsigned integer or pointer, the assignment
- statement generates a MULU (unsigned multiply) instruction.
-
- As another example, the assignment a += b. which adds b to a, generates
- and ADD.B (byte length add) instruction if a is a char, but it generates an
- ADD.W (word length add) instruction if a is a long word or pointer.
-
-
- ASSEMBLY-LANGUAGE INSTRUCTIONS AND PSEUDO-OPERATIONS
-
- Reserved identifiers also stand for 68000 machine-language instructions
- and pseudo-operations. A library of pseudofunctions must be linked with
- a PL/68K program when it is translated with a C compiler. This library,
- called the ops library, contains declarations and functions that allow C
- programs to simulate the effect of 68000 machine instructions and
- pseudo-operations. (See Table 4)
-
- The pseudofunction btst ( ), for example, simulates the BTST (bit test)
- machine instruction. In PL/68K, you would write btst(1,d0b); in those
- places where you would write btst.b#11,d0 in traditional 68000 assembly
- language.
-
- Other pseudofunctions allow PL/68K programs to refer to assembly-
- language pseudo-operations. The PL/68K assembler translates the org( ),
- even( ), bss( ), text(), and data( ) pseudofunctions to the ORG, EVEN
- BSS, TEXT, and DATA pseudo-operations. Similarly, the PL/68K
- assembler translates the dcb( ), dcw( ), dcl( ), dsb( ), dsw( ), and dsl( )
- pseudofunctions to the DC.B, DC.W, DC.L, DS.B, DS.W, and DS.L
- pseudo-operations. None of these pseudofunctions has any effect when a
- C compiler translates a PL/68K program. In other words, the
- corresponding pseudofunctions in the ops library do nothing.
-
- You may be wondering why I keep calling these routines
- pseudofunctions. After all, they are perfectly good functions when
- compiling a PL/68K program with C compiler. When you turn the
- program through the PL/68K assembler, though, it translates
- pseudofunctions directly in 68000 machine instructions or pseudo-
- operations.
-
-
- EXPRESSIONS AND ASSIGNMENT STATEMENTS
-
- I've covered the components of low-level assembly language--register,
- address modes and effective addresses, machine instructions, and
- pseudo-ops. Now let's see how you put these components together to
- make expressions and assignment statements.
-
- Expressions are just like C expressions, but they may not be arbitrarily
- complex. As the code selection rule requires, PL/68K defines what code
- arithmetic expressions will generate.
-
- (See Table 5.) The += and + operators generate the ADD instruction, the
- P= and P operators generate the SUB instruction, and so on. Many
- machine instructions on the 68000 have several variants--for example,
- ADDA adds address registers, ADDI adds literal data, and plain ADD
- adds to data registers and memory locations. The assembler has to do
- some code selection, but the choice is easy; even traditional assemblers do
- that much.
-
-
- BOOLEAN EXPRESSIONS
-
- Boolean expressions in PL/68K are similar to Boolean expressions in C.
- All the boolean operators !, | |, and && and all the relational operators ==,
- !=, <, <=, >, and >= are allowed.
-
- Boolean expressions can only appear in the appropriate part of if, do,
- while, and for statements. The code fragment a ==b; is not valid in
- PL/68K outside a structure statement. This restriction eliminates a whole
- class of hard-to-find errors (the programmer almost certainly meant to say
- a = b).
-
- Because Boolean expressions appear in limited contexts, less work is
- required to evaluate them. (See Tables 6 and 7) Boolean expressions
- generate the TST (test operand) or CMP (compare) instructions followed
- by some form of the Bxx (conditional branch) instruction. The Bxx
- instruction chosen depends on the relop and the declared type of the
- operand being tested. The NOT (!) operator generates no code at all but
- instead simply reverses the "polarity" of one or more BXX instructions.
- For example, the statement
-
- if(a == 0)
-
- generates
-
- TST A
- BNE
-
- while the statement
-
- if(!(a == 0))
-
- generates
-
- TST A
- BEQ
-
- Similarly, the | | and && operator generate no extra code. Incidentally,
- you can use parentheses in Boolean expressions to affect the order of
- binding of Boolean operators. For instance, the Boolean expression
-
- if(a && !(b<5 | |b>20))
-
- is valid and generates
-
- TST A
- BEQ
- CMPI 5, B
- BLT
- CMPI 20, B
- BGT
-
- You can specify condition code values directly. (See Table 8) For
- instance, the statement
-
- if (ccr=Z)
-
- tests the current value of the zero bit in the condition code
- register and generates the BNZ (branch not zero) instruction. Z is a
- macro in C, defined in the ops library, which expands to a call to the
- pseudofunction cc_z( ).
-
-
- STRUCTURE STATEMENTS
-
- I said earlier that PL/68K has all C's structure statements--if, do, while,
- for, and switch. They look exactly like C code, but curly braces are
- required surrounding statement lists in structure statements. In other
- words, structure statements have the form
-
- if(...) {statement list}
- if(...) {statement list} else {statement list}
- while(...) {statement list}
- do {statement list} while(...);
- for(...) {statement list}
- switch(...) {statement list}
-
- In my opinion, allowing curly braces to be optional is a big flaw in C. In
- this example:
-
- if(abc<5)
- xyz=5;
- if(abc<6)
- xyz=6;
-
- the indentation is misleading and will probably cause a bug. This kind of
- error can be extremely difficult to find.
-
- Let's see what code PL/68K's structure statements produce. In the
- accompanying tables, the dollar sign denotes code that corresponds to
- some language construct. for example,
-
- $ statement list $
-
- stands for whatever code is generated for the statement list. The statement
- list could be arbitrarily complicated--for instance, it could contain nested
- structure statements. the notation
-
- $ Evaluate boolean. If false, jump to label1 $
-
- indicates that code is generated for the Boolean expression such that a
- jump to label1 is taken if the Boolean expression is false, Otherwise,
- control falls through to the following code. Labels are indicated in the
- usual way, by identifiers followed by colons. All generated labels are, of
- course, unique, even though they may have identical names in the tables.
-
- Table 9 shows the if statement. When an if statement contains no else
- clause, the Boolean expression is evaluated, and control either falls
- through to the then clause or jump is make to the end of the statement.
-
- Similar code is generated when the if statement contains an else clause.
- the Boolean expression is evaluated, and control either falls through to the
- then clause or a jump is made to the else clause. A BRA (branch always)
- instruction following the then clause skips around the else clause.
-
- The code generated for the while statement, shown in Table 10 might be a
- little controversial. the first instruction is a branch to the end of the loop
- so that the loop test occurs at the bottom. This produces the fastest code
- unless the while loop is executed less than once on average. In the rare
- cases in which this jump is unwanted, the programmer must simulate the
- loop in some way.
-
- Notice the labels called continue_label and break_label. These are used as
- target labels for the break and continue statements. In other words,
- within a while, do, or for statement, the effect of a continue instruction is
- to generate a branch to the continue_label defined for that statement.
- Similarly, a break statement generates a jump to the appropriate
- break_label. As in C, you can also use the break statement inside a
- switch statement.
-
- The while statement generates different code if the Boolean expression is
- a nonzero constant. this is a common idiom in C, and the definition of
- PL/68K ensures that there is no time penalty for using it.
-
- The code for the do statement, shown in Table 11 is similar to the code
- produced by the while statement. The code for the for statement (See
- Table 12) is more interesting. If the loop test in a for statement is
- nontrivial, the code for it appears at the bottom of the loop. Note also that
- the syntax of the for statement is more restricted than in C.
-
- The switch statement, shown in Table 13 generates a jump table--i.e., a
- table of addresses. code is generated that jumps through that table to the
- proper case statement, based on the contents of a register. Note that the
- switch statement destroys this register.
-
- It is sometimes better to generate a sequence of tests rather than a table
- jump, but the case statement always generates a table jump. Remember,
- each language construct in PL/68K stands for a particular sequence of
- code--if you want a sequence of tests, use a sequence of if statements; if
- you want a table jump, use a switch statement.
-
- Many compilers generate jumps to jumps when they translate structure
- statement, but the definition of PL/68K requires that all jumps to jumps
- (and jumps to return statements) be eliminated. The assembler can do this
- in several ways. For instance, if the assembler creates a parse tree for an
- entire function before any code is generated, it's easy for the code
- generator to look at the target of any jump to see if it is another jump or a
- return instruction. Alternatively, the assembler can use a standard
- peephole optimizer.
-
-
- FUNCTION CALLS
-
- Functions in PL/68K can have formal parameters and local arguments,
- just as in C. The code shown in Table 14 is generated by function calls.
- Code is generated to push all arguments on the stack, a JSR (jump to
- subroutine) instruction is generated to pop arguments off the stack. One
- long word is always reserved on the stack for the first argument, which
- shortens the calling sequence when there are less than two arguments.
-
- The ADD instruction can be eliminated by having the called program,
- instead of the calling program, pop the arguments off the stack, but the
- sequence shown in Table 14 is the fastest. If you eliminated the ADD
- instruction and pushed the arguments in the same way (that is, above the
- return address), the called program would need to do much more work to
- pop off its arguments. You could also push the actual arguments below
- the return address, but that way actually increases the length of the calling
- sequence. In order to push arguments below the return address, you
- would have to use an instruction such as move arg,m(sp), which is 2
- bytes longer than move arg,P(sp).
-
- Unlike standard C, PL/68K does specify the order in which arguments
- are pushed, namely in reverse order. thus, a function that takes a variable
- number of arguments--printf( ) for instance--will find its first argument
- on the top of the stack.
-
- Of course, it is often best to pass arguments in registers, but PL/68K
- doesn't need a separate mechanism to do this. Suppose you have a
- function called g( ) whose two arguments are passed on the stack. to
- change g( ) so that it will take its arguments in registers, you must define
- the following macro
-
- #define g(a,b) d0=a; d1=b; g1()
-
- and change g's name to g1. Notice that a statement such as
-
- if(...) g(x,y);
-
- cannot cause problems in PL/68K because you must write
-
- if(...) {g(x,y);}
-
- instead. (If braces were omitted, after macro expansion, the code would
- be
-
- if(...)d0=a; d1=b;g1();
-
- and only the assignment d0=a would be part of the if statement.)
-
- This macro might generate redundant code. Suppose it were called with
- d0 as the first argument, for instance. The macro would expand to d0=d0
- and generate the instruction move d0,d0. to handle that problem, the
- PL/68K assembler eliminates redundant moves. If you must generate
- such a redundant move for some reason, use a pseudofunction.
- Pseudofunctions are never second-guessed by the assembler.
-
-
- ACCESSING VARIABLES WITHIN FUNCTIONS
-
- Now that I've covered the passing of arguments to a function, I'll explain
- how the function gets hold of those arguments. This is a complicated
- subject, so before getting involved in some messy details, let's handle the
- easy cases.
-
- First, PL/68K keeps its hands off all registers declared outside any
- function. These global registers can be accessed from within functions,
- but PL/68K never generates code to save or restore them. Consequently,
- you can prevent PL/68K from interfering with any register simply by
- declaring that register outside a function. By the way, the register
- keyword is not valid outside functions, but that does not prevent you
- from declaring register variables anywhere you wish, either in C or in
- PL/68K. For instance, you can declare a0 to be global simply by saying
-
- char *a0;
-
- outside any function.
-
- Second, except for these global registers, all registers used in a function
- are saved on entry and restored on exit from the function. the assembler
- uses the MOVEM.L (move multiply register. long) instruction for this
- purpose. Accessing register variables declared in a function is, of course,
- easy.
-
- Third, if a local variable is declared to be static, memory is allocated to
- that variable in static memory, not on the stack. No extra code is needed
- to access that variable, either on function entry or exit.
-
- Static internal variables are not very useful; because the values of static
- internals are retained between invocations of a function, static internals are
- destroyed by recursive function calls. Also, on many machines
- (including the 68000) accessing a variable in static memory is more
- expensive than accessing a "local auto" variable--that is, a local stack
- variable. At any rate, generating code for static internal variables is
- straightforward and is not affected by the following complications.
-
- Functions need to access two kinds of nonregister variables: formal
- parameters and local auto variables. Both types are allocated on the stack.
- You have three choices for how PL/68K generates code for these stack
- variables. You make your choice using one of three pseudofunctions:
- base(An), base(sp), or nobase( ). if none of these appears in a function,
- the default is base(sp).
-
- First, you can have PL/68K access stack variables via an address register
- that is different from the stack pointer, as shown in Table 15. If the
- base(An) pseudofunction appears anywhere in the function (where An is
- any address register except the stack pointer, a7), PL/68K generates a
- LINK (link and allocate) instruction on function entry and UNLK
- instruction on function exit. All stack variables are accessed via the
- address register named in the base(An) pseudofunction.
-
- Second, you can have PL/68K access stack variables via the stack
- pointer, as shown in Table 16. The assembler generates this kind of code
- if the base(a7) or base(sp) pseudofunction appears anywhere in the
- function. The code generated for sp-based functions is more complicated,
- but more efficient, than that for An-based functions. If an sp-based
- function contains no function calls, the stack pointer is not incremented on
- function entry and local auto variables are accessed using positive offsets
- from the stack pointer. If the function does contain other function call,
- however, space is reserved for local auto variables by incrementing the
- stack pointer on function entry, and local auto variables are accessed using
- negative offsets from the stack pointer.
-
- Third, you can access stack variables by hand. If the no_base( )
- pseudofunction occurs anywhere in the function, no code is generated on
- function entry or exit, declarations of stack variables are ignored, and no
- explicit references to stack variables are permitted. the no_base( )
- pseudofunction is useful when you must play some kind of game with the
- stack.
-
- Sp-based functions are somewhat dangerous. If the stack pointer is
- changed using a pseudofunction, the offsets used to access stack variables
- are going to get out of sync. to handle this problem, several
- pseudofunctions, shown in Table 17 allow you to indicate that the offsets
- should be changed.
-
- The push(arg) and pop(arg) pseudofunctions are equivalent to the
- move(arg,*P Psp) and move(*sp++,arg) pseudofunctions, but in
- addition, they tell the PL/68K assembler to adjust the offsets used to
- access stack variables in sp-based functions. The addsp(n) and subsp(n)
- pseudofunctions are equivalent to the addi(n,sp) and subi(n,sp)
- pseudofunctions, but again they tell the assembler to adjust offsets.
- Finally, the adjsp(n) pseudofunction generates no code but tells the
- assembler to adjust the offsets. These pseudofunctions have no effect on
- An-based functions.
-
-
- SUMMARY
-
- To summarize the strengths and weaknesses of PL/68K, the syntactical
- restrictions that make PL/68K a strict subset of C are listed as follows:
-
- o All operand/operator combinations correspond to an instruction in the
- 68000 instruction set.
-
- o Assignment statements are slightly restricted.
-
- o Statement lists must be surrounded by curly brackets.
-
- o There are no floating point or double constants or operators.
-
- There are no additions or changes to C syntax. PL/68K and assembly
- language are semantically identical; the advantages of PL/68K over
- assembly language are all syntactical:
-
- o C syntax makes programs easier to read.
-
- o C syntax eliminates many common coding errors.
-
- o Far fewer visible labels are required.
-
- PL/68K is not the successor to C nor is it superior to C for most
- programming projects. The advantages of PL/68K over C apply only in
- limited circumstances, but when performance is paramount, PL/68K
- stands out:
-
- o All machine resources and instructions are available.
-
- o Global allocation of registers is possible.
-
- o Total control over generated code is possible.
-
- o Generated code is smaller and faster.
-
- Figure 2 shows the relationship between PL/68K and its two parent
- languages. PL/68K is only a subset of C; not all of C is included. On the
- other hand, C must be augmented by the ops library in order to simulate
- all the machine resources of assembly language.
-
- Listing One shows an example of a PL/68K function. This function finds
- a name in a symbol table and returns information about that name in
- registers. Listing Two shows the code generated by the PL/68K
- assembler for that function.
-
-
- CONCLUSION
-
- PL/68K started life as a C-like assembly language for the 68000 chip.
- High-level assemblers are not new--a paper by Niklaus Wirth (Journal
- ACM 15, January 1, 1968) described a high-level assembly language for
- IBM 360 machines.
-
- My early thinking about PL/68K was influenced by Wirth's paper and by
- the register allocation and code selection principles. At that stage, I was
- interested in using compiler technology to create better assemblers. I saw
- no particular reason to make PL/68K a subset of C.
-
- I felt dissatisfied with the initial version of PL/68K; it was doomed to be
- "just another computer language." Not wanting to add another minor
- dialect to the Babel of computer languages, I decided to make PL/68K's
- syntax identical to C's. This was a lucky decision.
-
- From the first, PL/68K had to be able to name all machine resources,
- including machine instructions and assembly-language pseudo-ops. Early
- versions of the language allowed "raw" lines of assembly code to be
- interspersed with C-like lines of code. Although this approach had some
- merit, my decision to make PL/68K's syntax compatible with C
- convinced me to use functional notation to represent assembly-language
- features. This change was also fortunate.
-
- I began to speculate about what would happen if a PL/68K program were
- compiled with a C compiler. I asked myself, suppose the program
- produced by the C compiler and the program produced by the PL/68K
- assembler were semantically equivalent? Suddenly, PL/68K became not
- just another assembly language but a new way of using C.
-
- The principle of semantic equivalence guided a redesign of the language;
- any construct that violated this principle had to go. In addition, I invented
- the ops library so that C programs could simulate 68000 machine
- instructions. Along with the ops library, the concept of pseudofunctions
- was born, and the language was complete.
-
- PL/68K allows me to sidestep a question that has been haunting me ever
- since I started programming--whether to program in assembly language
- and accept the resulting inconveniences or to program in a high-level
- language and accept a final product that is larger and slower than it could
- be. PL/68K solves that dilemma.
-
- You can design and write a program in C, keeping in mind the possibility
- that you will convert it to PL/68K eventually. You can write difficult parts
- of the program, or parts of the program, you can produce a PL/68K
- version of the program, if desired, by rewriting or excluding those parts
- of the program that use full C. You then test the PL/68K version and
- improve its performance as much as you require.
-
- PL/68K hits precisely the right level of abstraction for systems
- programming. All the features of C allow you to design and code
- programs easily, but when you need to do low-level work, nothing stands
- in your way.
-
- A final thought--you could transfer most of the syntax and all the design
- rules of PL/68K to a similar language for other machines. In this limited
- sense, PL/68K is a machine-independent assembly language--PL/68K
- programs are much more portable than programs written in traditional
- assembly language.
-